import socket
import struct
import time
import KBEngine
import Functor
import KBEDebug as log
import GlobalConst


class Account(KBEngine.Proxy):
    """
    帐号实体
    客户端登录到服务端后，服务端将会自动创建这个实体，通过这个实体与客户端进行交互
    """

    def __init__(self):
        KBEngine.Proxy.__init__(self)
        # 当前激活的Avatar
        self.activeAvatar = None
        log.DEBUG_MSG('Account[%i]::__init__' % self.id)
        self.ip = None
        self.props = {}
        self.lastCreateIsFinish = True

    # region KBEngine method
    def onClientEnabled(self):
        """
        KBEngine method.
        该entity被正式激活为可使用， 此时entity已经建立了client对应实体， 可以在此创建它的
        cell部分。
        """
        self.client.pushAccountId(self.accountId)
        self.ip = socket.inet_ntoa(struct.pack('=I', self.clientAddr[0]))
        log.INFO_MSG(f'Account.onClientEnabled[{self.databaseInterfaceName}] entities enable. entityCall:{self.client}, clientIp: {self.ip} {self.accountId}')
        if self.accountId == 0:
            KBEngine.baseAppData['Lobby'].reqAccountId(self)

    def onLogOnAttempt(self, ip, port, password):
        """
        KBEngine method.
        如果在脚本中实现了此回调，这个函数在客户端尝试使用当前账号实体进行登录时触发回调。
        这种情况通常是实体存在于内存中处于有效状态，最明显的例子是用户A使用此账号登录了，用户B使用同一账号进行登录，此时回调触发。

        这个回调函数可以返回如下常量值：
        KBEngine.LOG_ON_ACCEPT：允许新的客户端与实体进行绑定，如果实体已经绑定了一个客户端，之前的客户端将被踢出。
        KBEngine.LOG_ON_REJECT：拒绝新的客户端与实体绑定。
        KBEngine.LOG_ON_WAIT_FOR_DESTROY：等待实体销毁后再进行客户端绑定。
        """
        log.INFO_MSG(f'Account[{self.id}].onLogOnAttempt: ip: {ip}, port: {port}, selfClient: {self.client}, clientAddr: {self.clientAddr[0]}')
        if self.activeAvatar is not None:
            # 即使触发到了这里，Account身上也没有client实体，所以不能直接self.giveClientTo(self.activeAvatar)
            if self.activeAvatar.client is not None:  # 存在两种情况：1、玩家短时间内断线重连；2、顶号
                # 为什么这里要将client给Account实体呢？因为客户端的登录流程是通过客户端的Account和服务端交互的
                self.activeAvatar.giveClientTo(self)

            # 不管怎样，上个avatar的实体都必须要被销毁。至于服务端为什么要延时销毁avatar主要是为了缓存下client实体，防止重复创建
            self.activeAvatar.accountEntity = None  # 解除关联，防止在Avatar的onDestroy方法中销毁了当前的Account实体
            self.activeAvatar.destroySelf()
            self.activeAvatar = None

        return KBEngine.LOG_ON_ACCEPT

    def onClientDeath(self):
        """
        KBEngine method.
        客户端对应实体已经销毁
        """
        log.DEBUG_MSG("Account[%i].onClientDeath:" % self.id)
        if self.activeAvatar is not None:
            self.activeAvatar.accountEntity = None
            self.activeAvatar = None

        self.destroy()

    def onDestroy(self):
        """
        KBEngine method.
        entity销毁
        """
        log.DEBUG_MSG("Account[%i].onDestroy." % (self.id))
        if self.activeAvatar is not None:
            self.activeAvatar.accountEntity = None
            self.activeAvatar.destroySelf()
        self.activeAvatar = None
    # endregion

    # region exposed method
    def reqAvatarList(self):
        """
        请求所有角色列表，不包括已经逻辑删除的角色
        """
        log.DEBUG_MSG(f'Account.reqAvatarList: size={len(self.characters)}')
        characters = []
        for character in self.characters:
            if character['dbid'] == 0 and character['state'] == 0:
                continue
            else:
                characters.append(character)
        self.characters = characters
        log.DEBUG_MSG(f'reqAvatarList: {characters}')
        self.client.onReqAvatarList(self.characters)

    def reqCreateAvatar(self, roleType, name):
        """
        客户端请求创建一个角色
        """
        # 走第三方登录时，name可能为空
        # if len(name) == 0:
        #     log.ERROR_MSG(f'Account[{self.id}].reqCreateAvatar error name is empty')
        #     return
        if self.lastCreateIsFinish is False:
            log.ERROR_MSG(f'Account.reqCreateAvatar last create avatar is not finish')
            return
        log.DEBUG_MSG(f'reqCreateAvatar: name[{name}]')
        self.props = {
            'roleType': roleType,
            'nickName': name,
            'createTime': int(time.time()),
            'playerId': 0
        }
        KBEngine.baseAppData['Lobby'].reqPlayerId(self)

    def selectAvatarGame(self, dbid):
        """
        客户端选择某个角色进入游戏
        """
        # 等待创角
        cur_time = int(time.time())
        if self.banEndTime > cur_time:
            self.client.cantLogin(self.banEndTime)
            return

        if dbid == 0:
            for character in self.characters:
                if character['dbid'] == 0 and character['state'] == 2:
                    self.reqCreateAvatar(character['roleType'], character['nickName'])
                    return
            log.ERROR_MSG(f'selectAvatarGame error. dbid=0 and state != 2')
            return
        else:
            for i in range(len(self.characters)):
                if self.characters[i]['dbid'] == dbid:
                    self.characters[i]['state'] = 2
                else:
                    if self.characters[i]['state'] == 2:
                        self.characters[i]['state'] = 0
            self.writeToDB()
        log.DEBUG_MSG(f"Account.selectAvatarGame: {dbid} {self.databaseInterfaceName}")
        # 注意：使用giveClientTo的entity必须是当前baseapp上的entity
        if self.activeAvatar is None:
            # 由于需要从数据加载角色，因此是一个异步过程，加载成功或者失败会调用__onAvatarCreated接口
            # 当角色创建好之后，account会调用giveClientTo将客户端控制权（可理解为网络连接与某个实体的绑定）切换到Avatar身上，
            # 之后客户端各种输入输出都通过服务器上这个Avatar来代理，任何proxy实体获得控制权都会调用onClientEnabled
            # Avatar继承了Teleport，Teleport.onClientEnabled会将玩家创建在具体的场景中
            # KBEngine.createEntityFromDBID('Avatar', dbid, self._onAvatarCreated) # , self.databaseInterfaceName)
            KBEngine.createEntityFromDBID('Avatar', dbid, self._onAvatarCreated, self.databaseInterfaceName)
        else:
            self.giveClientTo(self.activeAvatar)
    # endregion

    # region callback method
    def _onAvatarCreated(self, baseRef, dbid, wasActive):
        """
        选择角色进入游戏时被调用
        """
        if wasActive:
            log.ERROR_MSG("Account[%i]._onAvatarCreated: this character is in world now!" % self.id)
            return

        if baseRef is None:
            log.ERROR_MSG("Account[%i]._onAvatarCreated: the character you wanted to create is not exist!" % self.id)
            return

        avatar = KBEngine.entities.get(baseRef.id)
        if avatar is None:
            log.ERROR_MSG("Account[%i]._onAvatarCreated: when character was created, it died as well!" % self.id)
            return

        if self.isDestroyed:
            log.ERROR_MSG("Account[%i]._onAvatarCreated: i dead, will the destroy of Avatar!" % self.id)
            avatar.destroy()
            return

        avatar.accountEntity = self
        self.activeAvatar = avatar
        self.giveClientTo(avatar)

    def _onAvatarSaved(self, success, avatar):
        """
        新建角色写入数据库回调

        Avatar.__init__和其身上的组件的onAttached在这里之前被执行
        """
        self.lastCreateIsFinish = True
        log.INFO_MSG("Account[%i]._onAvatarSaved: create avatar state: %i, %s, %i" % (self.id, success, avatar.nickName, avatar.databaseID))
        # 如果此时帐号已经销毁，角色已经无法被记录则我们清除这个角色
        if self.isDestroyed:
            if avatar:
                avatar.destroy(True)
            return

        if success:
            avatarInfo = {
                'dbid': avatar.databaseID,
                'nickName': avatar.nickName,
                'roleType': avatar.roleType,
                'playerId': self.props['playerId'],
                'state': 2,
                'chapterId': 1,
                'level': 1
            }

            self.characters.append(avatarInfo)

            log.INFO_MSG(f'characters 0: {self.characters}')
            characters = []
            for character in self.characters:
                if character['dbid'] == 0 and character['state'] == 2:
                    continue
                else:
                    if character['state'] == 2 and character['dbid'] != avatar.databaseID:
                        character['state'] = 0
                    characters.append(character)
            self.characters = characters
            log.INFO_MSG(f'characters 1: {self.characters}')
            self.writeToDB()

            avatar.accountEntity = self
            self.activeAvatar = avatar
            self.giveClientTo(avatar)
        else:
            log.ERROR_MSG('Account[%i]._onAvatarSaved: create avatar faile.' % (self.id))

    # endregion

    def updateChapterIdLevel(self, dbid, new_chapter, new_level):
        for i in range(len(self.characters)):
            if self.characters[i]['dbid'] == dbid:
                self.characters[i]['chapterId'] = new_chapter
                self.characters[i]['level'] = new_level
                self.characters = self.characters
                return
        log.ERROR_MSG(f'updateChapterId[{dbid}]: not found')

    def updateChapterId(self, dbid, new_chapter):
        for i in range(len(self.characters)):
            if self.characters[i]['dbid'] == dbid:
                self.characters[i]['chapterId'] = new_chapter
                self.characters = self.characters
                return
        log.ERROR_MSG(f'updateChapterId[{dbid}]: not found')

    def updateLevel(self, dbid, new_level):
        for i in range(len(self.characters)):
            if self.characters[i]['dbid'] == dbid:
                self.characters[i]['level'] = new_level
                self.characters = self.characters
                return
        log.ERROR_MSG(f'updateLevel[{dbid}]: not found')

    def onReqAccountId(self, account_id):
        log.INFO_MSG(f'Account.onReqAccountId accountId: {account_id}')
        self.accountId = account_id
        self.client.pushAccountId(account_id)

    def onReqPlayerId(self, player_id):
        log.INFO_MSG(f'Account.onReqPlayerId playerId: {player_id}')
        # 将avatar创建在和account相同的baseapp上，这样它们之间的方法调用和属性访问不需要通过rpc的方式实现。
        self.props['playerId'] = player_id
        # 防止计算跨天数时loginTim为0导致巨大的计算量
        self.props['loginTime'] = int(time.time())
        self.client.onPushRegisterInfo(self.accountId, player_id)
        avatar = KBEngine.createEntityLocally('Avatar', self.props)
        if avatar:
            avatar.writeToDB(self._onAvatarSaved, False, self.databaseInterfaceName)
